package team.nmsg.ge.system.init.shiro;

import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.DispatcherType;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.session.mgt.DefaultSessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSubjectFactory;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;


@Configuration
public class ShiroConfig {
	private static final Log logger = LogFactory.getLog(ShiroConfig.class);
	/**
	 * ShiroFilterFactoryBean 处理拦截资源文件问题。
	 * 注意：单独一个ShiroFilterFactoryBean配置是会报错的，因为在初始化ShiroFilterFactoryBean的时候需要注入：SecurityManager
	 *
	 * Filter Chain定义说明 
	 * 1、一个URL可以配置多个Filter，使用逗号分隔 
	 * 2、当设置多个过滤器时，全部验证通过，才视为通过
	 * 3、部分过滤器可指定参数，如perms，roles
	 */
	
    @Bean  
    public FilterRegistrationBean filterRegistrationBean() {  
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();  
        filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));   
        filterRegistration.setEnabled(true);  
        filterRegistration.addUrlPatterns("/*");   
        filterRegistration.setDispatcherTypes(DispatcherType.REQUEST);  
        return filterRegistration;  
    }
	
	
	
    @Bean(name = "shiroFilter")
	public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager")DefaultWebSecurityManager securityManager, @Qualifier("statelessAuthcFilter") StatelessAccessControlFilter statelessAuthcFilter) {
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		// 必须设置 SecurityManager
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		//在shiro之前添加一个过滤器，对token进行过滤
		shiroFilterFactoryBean.getFilters().put("statelessAuthcFilter", statelessAuthcFilter);
		
		// 拦截器.
		Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();  //这里必须为LinkedHashMap， 因为它必须保证有序
		// 配置不会被拦截的链接 顺序判断
		filterChainDefinitionMap.put("/", "anon");  //anon 表示可以匿名访问， authc表示需要认证才可以访问
		filterChainDefinitionMap.put("/login", "anon");
		filterChainDefinitionMap.put("/refreshToken", "anon");
		filterChainDefinitionMap.put("/logout", "anon");
		filterChainDefinitionMap.put("/noAuth", "anon");
		filterChainDefinitionMap.put("/appResources/**", "anon");  //对于app的资源不需要进行认证
		filterChainDefinitionMap.put("/static/**", "anon");  
		filterChainDefinitionMap.put("/images/**", "anon");  //anon 表示可以匿名访问， authc表示需要认证才可以访问
		filterChainDefinitionMap.put("/resources/images/**", "anon");  //anon 表示可以匿名访问， authc表示需要认证才可以访问
		filterChainDefinitionMap.put("/resources/**", "anon");  //anon 表示可以匿名访问， authc表示需要认证才可以访问
		filterChainDefinitionMap.put("/resext/**", "anon");  //anon 表示可以匿名访问， authc表示需要认证才可以访问
		filterChainDefinitionMap.put("/guest/**", "anon");  //anon 表示可以匿名访问， authc表示需要认证才可以访问
		
//		filterChainDefinitionMap.put("/photo/**", "anon");  //anon 表示可以匿名访问， authc表示需要认证才可以访问
		
		filterChainDefinitionMap.put("/css/**", "anon"); 
		filterChainDefinitionMap.put("/js/**", "anon"); 
		filterChainDefinitionMap.put("/websocket/**", "anon"); 
		filterChainDefinitionMap.put("/**", "statelessAuthcFilter");
		
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
		logger.info("Shiro拦截器工厂类注入成功");
		return shiroFilterFactoryBean;
	}
	
    
    
    //Stateless的过滤器，主要是对token进行校验
    @Bean(name="statelessAuthcFilter")
    public StatelessAccessControlFilter statelessAuthcFilter(){
    	StatelessAccessControlFilter statelessAuthcFilter = new StatelessAccessControlFilter();
		return statelessAuthcFilter;
    	
    }
    
    //将自定义 Filter不注册到 ApplicationFilterChain
    @Bean
    public FilterRegistrationBean registration(StatelessAccessControlFilter filter) {
        FilterRegistrationBean registration = new FilterRegistrationBean(filter);
        registration.setEnabled(false);
        return registration;
    }
    
    
    
    //配置核心安全事务管理器
    @Bean(name="securityManager")
	public DefaultWebSecurityManager securityManager(@Qualifier("statelessSubjectFactory")DefaultWebSubjectFactory  statelessSubjectFactory ,@Qualifier("sessionManager") DefaultSessionManager sessionManager, @Qualifier("authRealm") SysAuthorizingRealm sysAuthorizingRealm) {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		//设置subjectFactory 为我们自己覆写的statelessSubjectFactory
		securityManager.setSubjectFactory(statelessSubjectFactory);
		
		//设置我们
		securityManager.setSessionManager(sessionManager);
		
		 /*
         * 禁用使用Sessions 作为存储策略的实现，但它没有完全地禁用Sessions
         * 所以需要配合context.setSessionCreationEnabled(false);
         */
		DefaultSessionStorageEvaluator dsse = (DefaultSessionStorageEvaluator)((DefaultSubjectDAO)securityManager.getSubjectDAO()).getSessionStorageEvaluator();
		dsse.setSessionStorageEnabled(false);
		
		// 设置realm.
		securityManager.setRealm(sysAuthorizingRealm);
		return securityManager;
	}
    
    
    /**
     * subject工厂管理器.
     * @return
     */
    @Bean(name="statelessSubjectFactory")
    public DefaultWebSubjectFactory subjectFactory(){
    	StatelessDefaultSubjectFactory statelessSubjectFactory = new StatelessDefaultSubjectFactory();
    	return statelessSubjectFactory;
    }
    
    /**
     * session管理器：
     * sessionManager通过sessionValidationSchedulerEnabled禁用掉会话调度器，
     * 因为我们禁用掉了会话，所以没必要再定期过期会话了。
     * @return
     */
    @Bean(name="sessionManager")
    public DefaultSessionManager sessionManager(){
       DefaultSessionManager sessionManager = new DefaultSessionManager();
       sessionManager.setSessionValidationSchedulerEnabled(false);
       return sessionManager;
    }
    
    
    
   
    
    
	
	/**
	 * 身份认证realm; (这个需要自己写，账号密码校验；权限等)
	 * @return
	 */
	@Bean(name="authRealm")
	public SysAuthorizingRealm trapServerAuthorizingRealm() {
		SysAuthorizingRealm trapServerAuthorizingRealm = new SysAuthorizingRealm();
		return trapServerAuthorizingRealm;
	}
	
	
	@Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
        return new LifecycleBeanPostProcessor();
    }
	
	@Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator creator=new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }
	
	
	@Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") DefaultWebSecurityManager manager) {
        AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(manager);
        return advisor;
    }
	

}
